#include "co2sensorMg811.h"
#include <Flash.h>

Co2SensorMg811::Co2SensorMg811(int thresholdLevelPin, int co2SensorPin)
{
  //Para ABC (automatic baseline calibration). El voltaje en el sensor es más alto cuanto menos CO2 hay en el ambiente.
  //El máximo valor leído se debería alcanzar con aire fresco (el CO2 por defecto en aire no viciado). OJO, sólo funciona
  //este método si nunca se elimina el CO2 por otros gases, por ejemplo echando nitrógeno puro sobre el sensor.
  m_highestMg811VoltageRecorded = ZERO_POINT_VOLTAGE;
  
  // two points are taken from the curve.  with these two points, a line is formed which is
  // "approximately equivalent" to the original curve.
  // data format:{ x, y, slope}; point1: (lg400, 0.324), point2: (lg4000, 0.280) 
  // slope = ( reaction voltage ) / (log400 ¨Clog1000) 
  m_CO2Curve[0] = 2.602;
  m_CO2Curve[1] = ZERO_POINT_VOLTAGE;
  m_CO2Curve[2] = REACTION_VOLTAGE / (2.602 - 3); //slope

  m_thresholdLevelPin = thresholdLevelPin;
  m_co2SensorPin = co2SensorPin;
}

void Co2SensorMg811::setup()
{
  DEBUG_PRINT(F("Inicializando mg811\n"));
  pinMode(m_co2SensorPin, INPUT);                        //set pin to input
  digitalWrite(m_co2SensorPin, HIGH);                    //turn on pullup resistors
  
    //Y los pines de entrada
  pinMode(m_thresholdLevelPin, INPUT);

  
  setupLevelLeds();
}

void Co2SensorMg811::setupLevelLeds()
{
  DEBUG_PRINT(F("Inicializando leds\n"));
  //Declaramos pines de salida
  pinMode(LED1_D_PIN, OUTPUT);
  pinMode(LED2_D_PIN, OUTPUT);
  pinMode(LED3_D_PIN, OUTPUT);
  pinMode(LED4_D_PIN, OUTPUT);
  pinMode(LED5_D_PIN, OUTPUT);
}

int Co2SensorMg811::getThresholdLevel()
{
  //Primero leemos el potenciómetro
  double threshold = analogRead(m_thresholdLevelPin);
  // Mapeamos los valores de [0,1023] a valores de ppm de CO2 legibles por el sensor
  // En lugar de mapear directamente de MIN_MEASURABLE_CO2_LEVEL a MAX_MEASURABLE_CO2_LEVEL,
  // lo hago mapeando a valores 10 veces menores y luego multiplicando por 10 el resultado
  // para evitar fluctuaciones al variar mínimamente la resistencia (es un rango de valores amplio
  // y una mínima fluctuación en la resistencia hace que se vea cambiar el número en la pantalla)
  int rangeMin = MIN_MEASURABLE_CO2_LEVEL / ALARM_PRECISSION;
  int rangeMax = MAX_MEASURABLE_CO2_LEVEL / ALARM_PRECISSION;
  threshold = map(threshold, 0, 1023, rangeMin, rangeMax) * ALARM_PRECISSION;
  return threshold;
}

void Co2SensorMg811::setLedStatus(int led1, int led2, int led3, int led4, int led5) 
{
  digitalWrite(LED1_D_PIN, led1);
  digitalWrite(LED2_D_PIN, led2);
  digitalWrite(LED3_D_PIN, led3);
  digitalWrite(LED4_D_PIN, led4);
  digitalWrite(LED5_D_PIN, led5);
}

int Co2SensorMg811::getPpm()
{
  double volts = getMg811RawRead();
  int co2ppm = getMg811ppm(volts, m_CO2Curve);
  
  if (co2ppm < MIN_MEASURABLE_CO2_LEVEL)
    co2ppm = LOWER_THAN_MIN_PPM;
  else if (co2ppm > MAX_MEASURABLE_CO2_LEVEL)
    co2ppm = HIGHER_THAN_MAX_PPM;
  
  return co2ppm;
}

/*****************************  GetMg811RawRead *********************************************
 * Input:   mg_pin - analog channel
 * Output:  output of SEN-000007
 * Remarks: This function reads the output of SEN-000007
 *          Output falls as CO2 level rises
 ************************************************************************************/
double Co2SensorMg811::getMg811RawRead()
{
  double v=0;
  for (int i=0 ; i < MEASURE_SAMPLES ; i++)
  {
    v += analogRead(m_co2SensorPin);
    delay(SAMPLE_READ_INTERVAL);
  }
  //Hacemos la media 
  v = (v / MEASURE_SAMPLES);
  //mapeamos el nivel medido en el pin (0-1023) a uno correspondiente entre [0V, 5V]
  v= v * (5.0 / 1024.0); 
  return v;  
}

/*****************************  GetMg811ppm **********************************
 * Input:   volts   - SEN-000007 output measured in volts
 * pcurve  - pointer to the curve of the target gas
 * Output:  ppm of the target gas
 * Remarks: By using the slope and a point of the line. The x(logarithmic value of ppm) 
 * of the line could be derived if y(MG-811 output) is provided. As it is a 
 * logarithmic coordinate, power of 10 is used to convert the result to non-logarithmic 
 * value.
 ************************************************************************************/
int  Co2SensorMg811::getMg811ppm(double volts, float *pcurve)
{
  int ppm = -1;
  //La fórmula se aplica con el voltaje del sensor sin tener en cuenta ganancia
  //del circuito de adaptación de señal
  double sensorVolts = volts / DC_GAIN; 
  
  if ( sensorVolts >= m_highestMg811VoltageRecorded)
  {
    m_highestMg811VoltageRecorded = sensorVolts; //Actualizamos el valor de umbral para "aire fresco"
    DEBUG_PRINT(F("########  Nuevo V maximo en sensor: "));
    DEBUG_PRINT(sensorVolts);
    DEBUG_PRINTLN();
  }
  else
  {
    double value = pow(10, ( sensorVolts - pcurve[1]) / pcurve[2] + pcurve[0]);
    //La precisión del sensor no es tan alta como para dar las ppm. Hacemos un truncado de forma que se den los valores
    //en escalones del valor de la precisión escogida
    value = (value / MEASURE_PRECISSION) * MEASURE_PRECISSION;
    ppm = value;
  }
  return ppm;
}

